package com.jdd.enterprise.util;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import com.fasterxml.jackson.databind.ser.FilterProvider;
import com.fasterxml.jackson.databind.ser.impl.SimpleBeanPropertyFilter;
import com.fasterxml.jackson.databind.ser.impl.SimpleFilterProvider;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.springframework.util.ObjectUtils;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.*;
/**
 * @Brief json工具类
 * @Author chenyl
 * @Date 2023-11-08
 * @Version 1.0.0
 */
public class JsonUtil {
    private static final ObjectMapper DEFAULT_OBJECT_MAPPER;
    static {
        DEFAULT_OBJECT_MAPPER = createMapper(JsonInclude.Include.ALWAYS);
    }
    /**
     * 通过inclusion创建ObjectMapper对象
     *
     * @param include Include枚举类
     * @return ObjectMapper对象
     */
    private static ObjectMapper createMapper(JsonInclude.Include include) {
        // 初始化Mapper
        ObjectMapper objectMapper = new ObjectMapper();
        // 为mapper注册一个带有BeanSerializerModifier的factory
        objectMapper.setSerializerFactory(objectMapper.getSerializerFactory().withSerializerModifier(new CustomBeanSerializerModifier()));
        // 设置输出时包含属性的风格
        objectMapper.setSerializationInclusion(include);
        // 设置时间格式
        objectMapper.setTimeZone(TimeZone.getTimeZone("GM+8"));
        objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
        objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
        // 解决jackson2无法反序列化LocalDateTime的问题
        objectMapper.registerModule(new JavaTimeModule());
        // 设置空对象不抛异常
        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        // 单引号处理
        objectMapper.configure(JsonParser.Feature.ALLOW_SINGLE_QUOTES, true);
        // 反序列化时，属性不存在的兼容处理
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        // 设置输入时忽略在json字符串中存在，但目标对象中不存在的属性
        objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        objectMapper.disable(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT);
        objectMapper.disable(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT);
        // 返回对象
        return objectMapper;
    }
    /**
     * 初始化mapper，定制化操作
     *
     * @return ObjectMapper对象
     */
    public static ObjectMapper mapper() {
        return createMapper(JsonInclude.Include.NON_NULL);
    }
    /**
     * 将json字符串转换成指定类的对象
     *
     * @param jsonStr json字符串
     * @param clazz   类名
     * @param <T>     泛型
     * @return 指定类的对象
     */
    public static <T> T parse(String jsonStr, Class<T> clazz) {
        if (ObjectUtils.isEmpty(jsonStr)) {
            return null;
        }
        try {
            ObjectMapper mapper = mapper();
            return clazz.equals(String.class) ? (T) jsonStr : mapper.readValue(jsonStr, clazz);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }
    /**
     * 将json字符串转换成指定类的集合
     *
     * @param jsonStr json字符串
     * @param clazz   类名
     * @param <T>     泛型
     * @return 指定类的集合
     */
    public static <T> List<T> parseArray(String jsonStr, Class<T> clazz) {
        if (ObjectUtils.isEmpty(jsonStr)) {
            return null;
        }
        try {
            ObjectMapper mapper = mapper();
            JavaType javaType = mapper.getTypeFactory().constructCollectionType(List.class, clazz);
            return mapper.readValue(jsonStr, javaType);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }
    /**
     * 将任意对象转换成map
     *
     * @param obj 任意对象
     * @return map
     */
    public static Map parseMap(Object obj) {
        if (ObjectUtils.isEmpty(obj)) {
            return new HashMap<>();
        }
        return mapper().convertValue(obj, Map.class);
    }
    /**
     * 将对象转换成json字符串
     *
     * @param src        对象
     * @param include    传入一个枚举值，设置输出属性
     * @param properties 需要过滤的属性
     * @param <T>        泛型
     * @return json字符串
     */
    public static <T> String toJson(T src, JsonInclude.Include include, String... properties) {
        if (ObjectUtils.isEmpty(src)) {
            return null;
        }
        if (src instanceof String) {
            return (String) src;
        }
        try {
            ObjectMapper mapper = ObjectUtils.isEmpty(include) ? DEFAULT_OBJECT_MAPPER : createMapper(include);
            if (StringUtil.isNotEmpty(properties)) {
                FilterProvider fp = new SimpleFilterProvider().addFilter("customFilter", SimpleBeanPropertyFilter.serializeAllExcept(properties));
                mapper.setFilterProvider(fp);
            }
            return mapper.writeValueAsString(src);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }
    /**
     * 将对象转换成json字符串
     *
     * @param src 对象
     * @param <T> 泛型
     * @return json字符串
     */
    public static <T> String toJson(T src) {
        return toJson(src, null, (String[]) null);
    }
    /**
     * 将对象转换成json字符串
     *
     * @param src        对象
     * @param properties 需要过滤的属性
     * @param <T>        泛型
     * @return json字符串
     */
    public static <T> String toJson(T src, String... properties) {
        return toJson(src, null, properties);
    }
    /**
     * 当序列化类型为Array，list，set时，且值为空时，序列化为[]
     * 当序列化类型为数值时，且值为空时，序列化为0
     * 当序列化类型为字符时，且值为空时，序列化为“”
     */
    private static class CustomBeanSerializerModifier extends BeanSerializerModifier {
        // 数组及集合序列化实现类
        private final JsonSerializer<Object> arrayJsonSerializer = new ArrayJsonSerializer();
        // 字符串序列化实现类
        private final JsonSerializer<Object> stringJsonSerializer = new StringJsonSerializer();
        // 数字序列化实现类
        private final JsonSerializer<Object> numberJsonSerializer = new NumberJsonSerializer();
        @Override
        public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
            // 遍历写入流设置序列化属性
            for (BeanPropertyWriter writer : beanProperties) {
                if (isNumber(writer)) {
                    writer.assignNullSerializer(numberJsonSerializer);
                }
                if (isString(writer)) {
                    writer.assignNullSerializer(stringJsonSerializer);
                }
                if (isArray(writer)) {
                    writer.assignNullSerializer(arrayJsonSerializer);
                }
            }
            return beanProperties;
        }
        /**
         * 判断是否为数字类型
         *
         * @param writer bean属性写入流
         * @return Boolean
         */
        boolean isNumber(BeanPropertyWriter writer) {
            Class<?> clazz = writer.getType().getRawClass();
            return clazz.equals(Short.class)
                    || clazz.equals(Integer.class)
                    || clazz.equals(Float.class)
                    || clazz.equals(Double.class)
                    || clazz.equals(Long.class)
                    || clazz.equals(BigDecimal.class);
        }
        /**
         * 判断是否为字符串类型
         *
         * @param writer bean属性写入流
         * @return Boolean
         */
        boolean isString(BeanPropertyWriter writer) {
            Class<?> clazz = writer.getType().getRawClass();
            return clazz.equals(Character.class)
                    || clazz.equals(String.class)
                    || clazz.equals(StringBuilder.class)
                    || clazz.equals(StringBuffer.class);
        }
        /**
         * 判断是否为日期类型
         *
         * @param writer bean属性写入流
         * @return Boolean
         */
        boolean isDate(BeanPropertyWriter writer) {
            Class<?> clazz = writer.getType().getRawClass();
            return clazz.equals(Date.class)
                    || clazz.equals(LocalDate.class)
                    || clazz.equals(LocalTime.class)
                    || clazz.equals(LocalDateTime.class);
        }
        /**
         * 判断是否为数组类型
         *
         * @param writer bean属性写入流
         * @return Boolean
         */
        boolean isArray(BeanPropertyWriter writer) {
            Class<?> clazz = writer.getType().getRawClass();
            return clazz.isArray()
                    || clazz.equals(List.class)
                    || clazz.equals(Set.class);
        }
    }
    /**
     * 数组及集合序列化实现类
     */
    private static class ArrayJsonSerializer extends JsonSerializer<Object> {
        @Override
        public void serialize(Object value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            if (ObjectUtils.isEmpty(value)) {
                jsonGenerator.writeStartArray();
                jsonGenerator.writeEndArray();
            } else {
                jsonGenerator.writeObject(value);
            }
        }
    }
    /**
     * 字符串序列化实现类
     */
    private static class StringJsonSerializer extends JsonSerializer<Object> {
        @Override
        public void serialize(Object value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            if (ObjectUtils.isEmpty(value)) {
                jsonGenerator.writeString("");
            } else {
                jsonGenerator.writeObject(value);
            }
        }
    }
    /**
     * 数字序列化实现类
     */
    private static class NumberJsonSerializer extends JsonSerializer<Object> {
        @Override
        public void serialize(Object value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            if (ObjectUtils.isEmpty(value)) {
                jsonGenerator.writeNumber(0);
            } else {
                jsonGenerator.writeObject(value);
            }
        }
    }
}
